﻿/*
 * 1、JWTSettings在appsettings.json中进行配置(访问Token和刷新token不同点主要在于 1）私钥、2）过期时间)
    "JWTSettings": {
        "ValidateIssuerSigningKey": true, // 是否验证密钥，bool 类型，默认true
        "IssuerSigningKey": "", // 自定义密钥，string 类型，必须是复杂密钥，长度大于16
        "ValidateIssuer": true, // 是否验证签发方，bool 类型，默认true
        "ValidIssuer": "", // 签发方，string 类型
        "ValidateAudience": true, // 是否验证签收方，bool 类型，默认true
        "ValidAudience": "", // 签收方，string 类型
        "ValidateLifetime": true, // 是否验证过期时间，bool 类型，默认true，建议true
        "ExpiredTime": 20, // 过期时间，long 类型，单位分钟，默认20分钟
        "ClockSkew": 5, // 过期时间容错值，long 类型，单位秒，默认 5秒
        "Algorithm": "HS256" // 加密算法，string 类型，默认 HS256
    },
    "JWTRefreshSettings": {
        "SigningKey":"",  //用于刷新Token的密钥
        "ExpiredTime": 1440 //过期时间 ， 默认1440分钟（24小时）
    }
 * 
 * 2、安装Nuget包
    > Microsoft.AspNetCore.Authentication.JwtBearer    //用于验证token
    > Microsoft.IdentityModel.Tokens      //用于签发token
 * 
 * 3、
 * 
 * **/


using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;

namespace Max.PH6_2006A.LowCode.API.Auth
{
    public class JWTHelper
    {
        readonly IConfiguration? _configuration;
        JWTSettingsOptions _settings;

        //public JWTHelper() { }
        public JWTHelper()
        {
            //采用这种方式，就不需要构造自动注入IConfiguration，但需要安装包：Microsoft.Extensions.Configuration.Json
            //ReloadOnChange = true表示appsetting.json文件发生改变后，会重新加载
            _configuration = new ConfigurationBuilder()
                 .Add(new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource { 
                     Path = "appsettings.json", 
                     ReloadOnChange = true })
                 .Build();

            _settings = this.SetValidationParameters();
        }

        #region 创建Token

        public string CreateToke(Dictionary<string, object> cliamDictionary, TokenType type)
        {
            //访问token
            if (type == TokenType.Access)
            {
                return IssueToken(cliamDictionary, _settings.ExpiredTime, _settings.SigningKey);
            }
            else
            {
                //刷新token
                return IssueToken(cliamDictionary, _settings.RefreshExpiredTime, _settings.RefreshSigningKey);
            }
        }

        //签发token
        private string IssueToken(Dictionary<string, object> cliamDictionary, double expires, string key)
        {
            //声明
            var cliams = new Claim[] { };
            foreach (var item in cliamDictionary)
            {
                cliams.Append(new Claim(item.Key.ToString(), item.Value.ToString()));
            }

            var signing = GetSigningCredentials(key);
            var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
                 issuer: _settings.Issuer,
                 audience: _settings.Audience,
                 claims: cliams,
                 expires: DateTime.Now.AddSeconds(expires), //这是秒为单位
                 notBefore: null, //为null表示立即生效  DateTime.Now.AddMilliseconds(30) //30s后生效
                 signingCredentials: signing
                );

            var jwtToke = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(token);

            return jwtToke;
        }

        //jwt的签名
        private Microsoft.IdentityModel.Tokens.SigningCredentials GetSigningCredentials(string key)
        {
            //这个就是头部声明的签名加密算法
            string algorithm = _settings.Algorithm;

            //自己定义的秘钥（secret）,最少128位，最大256位
            //string key = _settings.SigningKey;
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(key);
            Microsoft.IdentityModel.Tokens.SecurityKey secret = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(bs);

            var sign = new Microsoft.IdentityModel.Tokens.SigningCredentials(secret, algorithm);

            return sign;
        }


        //刷新token
        //调用刷新token来生成新的访问token,需要先判断刷新token是否过期，如果刷新token过期了则需要登录
        public string RefreshToken(string refreshToken)
        {
            var a = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().ReadJwtToken(refreshToken);

            if (a.ValidTo > System.DateTime.UtcNow)
            {
                Dictionary<string, object> clas = new Dictionary<string, object>();
                var c = a.Claims;
                c.ToList().ForEach(x =>
                {
                    clas.Add(x.ValueType, x.Value);
                });
                CreateToke(clas, TokenType.Access);
            }
            return "";
        }

        #endregion

        #region 验证Token的参数
        public JWTSettingsOptions SetValidationParameters()
        {
            JWTSettingsOptions options = new JWTSettingsOptions()
            {
                ValidateAudience = true,
                Audience = _configuration?.GetSection("JWTAccessSettings:Audience").Value,

                ValidateIssuer = true,
                Issuer = _configuration?.GetSection("JWTAccessSettings:Issuer").Value,

                ValidateIssuerSigningKey = true,
                SigningKey = _configuration?.GetSection("JWTAccessSettings:SigningKey").Value,

                ValidateLifetime = true,
                ExpiredTime = double.Parse(_configuration.GetSection("JWTAccessSettings:ExpiredTime").Value),
                Algorithm = _configuration.GetSection("JWTAccessSettings:Algorithm").Value,

                RefreshExpiredTime = double.Parse(_configuration.GetSection("JWTRefreshSettings: ExpiredTime").Value),
                RefreshSigningKey = _configuration.GetSection("JWTRefreshSettings: SigningKey").Value
            };

            return options;
        }

        #endregion

    }

    #region 自定义验证Token
    public class JWTValidation
    {

    }

    #endregion

    #region 验证参数模型

    public enum TokenType
    {
        Access, //访问
        Refresh //刷新
    }

    /// <summary>
    /// JWT验证Token的参数模型
    /// </summary>
    public class JWTSettingsOptions
    {
        /// <summary>
        /// 验证受众
        /// </summary>
        public bool ValidateAudience { get; set; } = true;

        /// <summary>
        /// 验证签发人
        /// </summary>
        public bool ValidateIssuer { get; set; } = true;

        /// <summary>
        /// 验证是否过期
        /// </summary>
        public bool ValidateLifetime { get; set; } = true;

        /// <summary>
        /// 验证签名加密的key(自定义的key)
        /// </summary>
        public bool ValidateIssuerSigningKey { get; set; } = true;

        /// <summary>
        /// 生成token时用到的受众
        /// </summary>
        public string? Audience { get; set; }

        /// <summary>
        /// 生成token时用到的签发人
        /// </summary>
        public string? Issuer { get; set; }

        /// <summary>
        /// 自定义的密钥
        /// </summary>
        public string? SigningKey { get; set; }

        /// <summary>
        /// 过期时间
        /// </summary>
        public double ExpiredTime { get; set; }

        /// <summary>
        /// 算法
        /// </summary>
        public string Algorithm { get; set; } = SecurityAlgorithms.HmacSha256;


        #region 刷新token的配置

        private string _refreshSigningKey;
        /// <summary>
        /// 刷新toke的Key
        /// </summary>
        public string RefreshSigningKey
        {
            get { return _refreshSigningKey; }
            set
            {
                if (string.IsNullOrEmpty(value))
                    _refreshSigningKey = this.SigningKey;
                else
                    _refreshSigningKey = value;
            }
        }


        private double _refreshExpiredTime;
        /// <summary>
        /// 刷新token的过期时间
        /// </summary>
        public double RefreshExpiredTime
        {
            get { return _refreshExpiredTime; }
            set
            {
                if (value <= 0)
                    _refreshExpiredTime = this.ExpiredTime;
                else
                    _refreshExpiredTime = value;
            }
        }

        #endregion
    }

    #endregion


}
